Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Render dictionaries in the positional argument of a tag as tag attributes #13

Merged
merged 5 commits into from
Feb 4, 2022

Conversation

cpsievert
Copy link
Collaborator

Closes #10

Comment on lines 272 to 295
for k, v in __m.items():
val = self._normalize_attr_value(v)
if val is None:
continue
attrs[self._normalize_attr_name(key)] = val_
nm = self._normalize_attr_name(k)

sep = " "
if isinstance(val, HTML):
sep = HTML(sep)

final_val = val
attr_val = attrs.get(nm)
if attr_val:
final_val = attr_val + sep + final_val

attrs[nm] = final_val

for k, v in attrs.items():
self_v = self.get(k)
if self_v:
sep = " "
if isinstance(v, HTML):
sep = HTML(sep)
attrs[k] = self_v + sep + v
Copy link
Collaborator

@wch wch Feb 3, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the string concatenation should go in the constructor (or maybe the Tag function?), instead of in the update method. For example, this makes sense:

div({"class": "a"}, class_="b")
#> <div class="a b"></div>

But this would be surprising, and make it difficult (or impossible?) to replace attributes with a new value:

x = div(class_="a")
x.attrs.update(class_="b")
x
#> <div class="a b"></div>

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It'd still be possible via __setitem__, but I suppose it'd be confusing in that it differs from usual update() behavior

@wch
Copy link
Collaborator

wch commented Feb 3, 2022

As a general note, we want to support stuff like this:

def foo(*args, **kwargs):
    return div(*args, kwargs, {"class": "foo1"}, class_="foo2" )

def bar(*args, **kwargs):
    return foo(*args, kwargs, {"class": "bar1"}, class_="bar2" )

bar(class_="myclass")
#> <div class="myclass bar1 bar2 foo1 foo2"></div>

But we also want to keep the original .update() behavior.

htmltools/core.py Show resolved Hide resolved
htmltools/core.py Outdated Show resolved Hide resolved
Comment on lines 273 to 274
# Preserve the HTML() when combining two HTML() attributes
sep = HTML(" ")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that if we're combining an HTML-marked and non-HTML-marked attribute, the result should be an HTML-marked attribute (and the non-HTML attribute should be escaped immediately).

For example, with this code:

div({"foo": HTML("a<b")}, foo="c<d")

It would be reasonable to expect the result to be:

<div foo="a<b c&lt;d"></div>

But as currently written in this PR, it would be:

<div foo="a&lt;b c&lt;d"></div>

However, now that I think about it, it may make sense to do this in a separate PR and change how adding HTML() objects works in general (not just here).

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, if we do want this behavior, we should have tests for it.

Copy link
Collaborator Author

@cpsievert cpsievert Feb 4, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that behavior would be sensible, but it seems like it'd add a fair amount of complexity to get similar behavior when concatenating HTML/non-HTML in general, which is the more common case (BTW, we don't support HTML() attributes in R):

>>> div(HTML("&") + " &")
<div>&amp; &amp;</div>

So, given that we're not sure that's something we want to support in general, I think it makes sense to keep things this way for consistency. I filed an issue about it #15

htmltools/core.py Outdated Show resolved Hide resolved
htmltools/core.py Show resolved Hide resolved
htmltools/core.py Outdated Show resolved Hide resolved
@wch wch merged commit dcebb4e into main Feb 4, 2022
@wch wch deleted the dict-attrs branch February 4, 2022 00:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Need easier way to pass along kwargs with same name as a named arg
2 participants